<template>
  <div class="map">
    <div id="inputIdDiv">
      <form id = "myForm" @submit.prevent="getInputId">
        <input type="text" name="myInput" id="myInput" placeholder="输入病历编号">
        <button type="submit" id="myButton">确定</button>
      </form>
    </div>
    <h1 style="font-size:32px;margin-bottom:0px;text-align:center;margin-left:40px;">病历图谱</h1>
    <div style="text-align: center; position:relative;">
      <svg width="1200" height="800" style="margin-right:80px;margin-bottom:-40px;" id="svg1">
      </svg>
      <!-- 绘制图例 -->
      <div id="indicator">
      </div>
      <!-- 绘制模式选择 -->
      <div id="mode">
        <span class="active" style="border-top-right-radius:0;border-bottom-right-radius:0;">节点</span>
        <span
          style="border-top-left-radius:0;border-bottom-left-radius:0;position:relative;left:-5px;">文字</span>
      </div>
      <!-- 绘制搜索框 -->
      <div id="search">
        <input type="text" class="form-control" placeholder="输入搜索内容">
      </div>
      <!-- 绘制右边显示结果 -->
      <div id="info">
        <h4></h4>
      </div>
    </div>

  </div>

</template>

<script>
import * as d3 from '../../../static/js/js_d3.v4.min.js'
import $ from 'jquery'
import axios from 'axios'

var dragging = false
var nodes = []
var links = []
var data = []
var nodeSet = []
var id = 0
export default {
  name: 'CaseGraphicGraph',
  mounted () {
    this.getURLId()
  },
  // data () {
  //   return {
  //     // nodes: [],
  //     // links: [],
  //     // data: [],
  //     // nodeSet: [],
  //     dragging: false,
  //     id: '1'
  //   }
  // },
  methods: {
    getInputId () {
      console.log('test')
      const myForm = document.getElementById('myForm')
      const inputValue = myForm.elements.myInput.value
      myForm.elements.myInput.value = ''
      console.log(inputValue)
      if (inputValue != null && inputValue !== '') {
        const isNumber = isNaN(inputValue)
        if (!isNumber) {
          id = parseInt(inputValue)
          this.getdata()
        }
      }
    },

    getURLId () {
      try {
        const urlParams = new URLSearchParams(window.location.search)
        if (id !== undefined) {
          id = urlParams.get('id')
          if (id === undefined || id == null) {
            this.generateGraph()
          } else {
            this.getdata()
          }
        }
      } catch (error) {
        console.log(error)
      }
    },

    generateGraph () {
      document.getElementById('svg1').innerHTML = ''
      document.getElementById('indicator').innerHTML = ''
      if (data.length === 0) {
        return
      }
      var svg = d3.select('#svg1')
      var width = svg.attr('width')
      var height = svg.attr('height')

      var names = ['病人', '病种', '病症', '治疗方法', '治疗内容', '既往病史和用药']
      var labels = ['Patient', 'Disease', 'Symptom', 'Therapy', 'TreatmentContent', 'History']
      var colors = ['#55ffff', '#ca635f', '#4e88af', '#aaaaff', '#02fa06', '#dacc39']
      var radius = [70, 50, 25, 40, 45, 20]
      var collisionRadius = 70

      // 背景颜色设置 补充CSS样式设置字体布局
      for (var i = 0; i < names.length; i++) {
        $('#indicator').append("<div><span style='background-color:" + colors[i] + "'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp</span>" + '&nbsp;&nbsp;&nbsp' + names[i] + '</div>')
      }
      // 利用d3.forceSimulation()定义关系图 包括设置边link、排斥电荷charge、关系图中心点
      var simulation = d3.forceSimulation()
        .force('link', d3.forceLink().id(d => {
          // console.log(d)
          return d.id
        }))
        .force('charge', d3.forceManyBody())
        .force('center', d3.forceCenter(width / 2, height / 2))
        .force('collision', d3.forceCollide(collisionRadius)) // 碰撞检测

      // D3映射数据至HTML中
      // g用于绘制所有边,selectALL选中所有的line,并绑定数据data(graph.links),enter().append("line")添加元素
      // 数据驱动文档,设置边的粗细
      var link = svg.append('g').attr('class', 'links').selectAll('line').data(links).enter()
        .append('line').attr('stroke', 'white').attr('stroke-width', function (d) {
          return 1.5 // 所有线宽度均为2
        })
      // });

      // 添加所有的点
      // selectAll("circle")选中所有的圆并绑定数据,圆的直径为d.size
      // 再定义圆的填充色,同样数据驱动样式,圆没有描边,圆的名字为d.id
      // call()函数：拖动函数,当拖动开始绑定dragstarted函数，拖动进行和拖动结束也绑定函数
      var node = svg.append('g').attr('class', 'nodes').selectAll('circle').data(nodes).enter()
        .append('circle').style('stroke', 'white').attr('r', function (d) {
          // 每次访问nodes的一项数据
          // console.log(d)
          let size = radius[0]
          switch (d.label) {
            case labels[0]:
              break
            case labels[1]:
              size = radius[1]
              break
            case labels[2]:
              size = radius[2]
              break
            case labels[3]:
              size = radius[3]
              break
            default:
              size = radius[4]
          }
          return size
        }).attr('fill', function (d) {
          for (let i = 0; i < labels.length; i++) {
            if (d.label === labels[i]) return colors[i]
          }
        }).attr('stroke', 'none').attr('name', function (d) {
          return d.properties.name
        }).attr('id', d => d.id)
        .call(d3.drag()
          .on('start', dragstarted)
          .on('drag', dragged)
          .on('end', dragended)
        )

      // 显示所有的文本
      // 设置大小、填充颜色、名字、text()设置文本
      // attr("text-anchor", "middle")设置文本居中
      var text = svg.append('g').attr('class', 'texts').selectAll('text').data(nodes).enter()
        .append('text').attr('font-size', function (d) {
          return 13
        }).attr('fill', function (d) {
          for (let i = 0; i < labels.length; i++) {
            if (d.label === labels[i]) return 'rgb(0,0,0)'
          }
        }).attr('name', function (d) {
          return d.properties.name
        }).text(function (d) {
          return d.properties.name
        }).call(d3.drag()
          .on('start', dragstarted)
          .on('drag', dragged)
          .on('end', dragended)
        )

      // 圆增加title
      node.append('title').text(d => d.properties.name)

      // simulation中ticked数据初始化并生成图形
      simulation.nodes(nodes).on('tick', ticked)

      simulation.force('link')
        .links(links)
        .distance(d => { // 每一边的长度
          let distance = 10
          console.log(d.source.label)
          switch (d.source.label) {
            case labels[0]:
              distance += 30
              break
            case labels[1]:
              distance += 25
              break
            case labels[2]:
              distance += 20
              break
            default:
              distance += 15
              break
          }
          switch (d.target.label) {
            case labels[0]:
              distance += 30
              break
            case labels[1]:
              distance += 25
              break
            case labels[2]:
              distance += 20
              break
            default:
              distance += 15
              break
          }
          return distance
        })

      // ticked()函数确定link线的起始点x、y坐标 node确定中心点 文本通过translate平移变化
      // ticked()函数确定link线的起始点x、y坐标 node确定中心点 文本通过translate平移变化
      function ticked () {
        link
          .attr('x1', function (d) {
            return d.source.x
          })
          .attr('y1', function (d) {
            return d.source.y
          })
          .attr('x2', function (d) {
            return d.target.x
          })
          .attr('y2', function (d) {
            return d.target.y
          })

        node
          .attr('cx', function (d) {
            return d.x
          })
          .attr('cy', function (d) {
            return d.y
          })

        text.attr('transform', function (d) {
          let size = 15
          switch (d.label) {
            case labels[0]:
              break
            case labels[1]:
              size = 14
              break
            case labels[2]:
              size = 13
              break
            default:
              size = 12
              break
          }
          return 'translate(' + (d.x - size / 2) + ',' + (d.y + size / 2) + ')'
        })
      }

      // 拖动函数代码
      // 开始拖动并更新相应的点
      function dragstarted (d) {
        if (!d3.event.active) simulation.alphaTarget(0.3).restart()
        d.fx = d.x
        d.fy = d.y
        dragging = true
        d3.select('#svg1 .nodes').selectAll('circle').attr('class', '')
        d3.select('#svg1 .links').selectAll('line').attr('class', '')
        d3.select('#svg1 .texts').selectAll('text').attr('class', '')
      }
      // 拖动进行中
      function dragged (d) {
        d.fx = d3.event.x
        d.fy = d3.event.y
      }
      // 拖动结束
      function dragended (d) {
        if (!d3.event.active) simulation.alphaTarget(0)
        d.fx = null
        d.fy = null
        dragging = false
      }

      // span点击事件
      $('#mode span').click(function (event) {
        // span都设置为不激活状态
        $('#mode span').removeClass('active')
        // 点击的span被激活
        $(this).addClass('active')
        // text隐藏 nodes显示
        if ($(this).text() === '节点') {
          // $('.texts text').hide()
          $('.nodes circle').show()
        } else {
          // $('.texts text').show()
          $('.nodes circle').hide()
        }
      })
      // 设置鼠标选中节点显示，并循环设置与选中节点相关联的节点显示
      // 为svg1父元素下的.nodes circle元素绑定鼠标进入事件
      $('#svg1').on('mouseenter', '.nodes circle', function (event) {
        // console.log(event)
        // 通过变量dragging保证拖动鼠标时，其状态不受影响，从而改变图形
        // 鼠标没有拖动才能处理事件
        if (!dragging) {
          // 获取被选中元素的名字
          var name = $(this).attr('name')
          var id = $(this).attr('id')
          // 设置#info h4样式的颜色为该节点的颜色，文本为该节点name
          // $(this).attr('fill')表示当前悬浮圆的填充色
          $('#info h4').css('color', $(this).attr('#222222')).text(name)
          // 每次点击添加属性前把上次显示的信息去除，否则会不断叠加
          $('#info p').remove()
          // 遍历查找id对应的属性
          for (let item of nodes) {
            if (item.id === id) {
              for (var key in item.properties) { // 显示值及其字段名字
                $('#info').append('<p><span>' + key + '</span>' + item.properties[key] +
                  '</p>')
              }
            }
          }
          // 选择#svg1 .nodes中所有的circle，再增加个class
          d3.select('#svg1 .nodes').selectAll('circle').attr('class', function (d) {
            // 数据的id是否等于name,返回空
            if (d.properties.name === name) {
              return ''
            } else { // 当前节点返回空，否则其他节点循环判断是否被隐藏起来(CSS设置隐藏)
              // links链接的起始节点进行判断,如果其id等于name则显示这类节点
              // 注意: graph=data
              for (var i = 0; i < links.length; i++) {
                // 如果links的起点等于name，并且终点等于正在处理的则显示
                if (links[i]['source'].properties.name === this.name && links[i]['target']
                  .id === d.id) {
                  return ''
                }
                if (links[i]['target'].properties.name === name && links[i]['source']
                  .id === d.id) {
                  return ''
                }
              }
              return 'inactive' // 前面CSS定义 .nodes circle.inactive
            }
          })
          d3.select('#svg1 .texts').selectAll('text').attr('class', function (d) {
            if (d.properties.name === name) {
              return ''
            }

            for (var i = 0; i < links.length; i++) {
              if (links[i]['source'].properties.name === name && links[i]['target']
                .id === d.id) {
                return ''
              }
              if (links[i]['target'].properties.name === name && links[i]['source']
                .id === d.id) {
                return ''
              }
            }
            return 'inactive'
          })
          // 处理相邻的边line是否隐藏 注意 ||
          d3.select('#svg1 .links').selectAll('line').attr('class', function (d) {
            if (d.source.properties.name === name || d.target.properties.name === name) {
              return ''
            } else {
              return 'inactive'
            }
          })
        }
      })
      // 鼠标移开还原原图，显示所有隐藏的点及边
      $('#svg1').on('mouseleave', '.nodes circle', function (event) {
        d3.select('#svg1 .nodes').selectAll('circle').attr('class', '')
        d3.select('#svg1 .links').selectAll('line').attr('class', '')
        d3.select('#svg1 .texts').selectAll('text').attr('class', '')
      })
      // 鼠标进入文本显示相邻节点及边
      $('#svg1').on('mouseenter', '.texts text', function (event) {
        if (!dragging) {
          var name = $(this).attr('name')

          $('#info h4').css('color', $(this).attr('fill')).text(name)
          $('#info p').remove()
          // 遍历查找id对应的属性
          for (let item of nodes) {
            if (item.properties.name === name) {
              for (var key in item.properties) { // 显示值及其字段名字
                $('#info').append('<p><span>' + key + '</span>' + item.properties[key] +
                  '</p>')
              }
            }
          }
          d3.select('#svg1 .nodes').selectAll('circle').attr('class', function (d) {
            // 数据的id是否等于name,返回空
            if (d.properties.name === name) {
              return ''
            } else { // 当前节点返回空，否则其他节点循环判断是否被隐藏起来(CSS设置隐藏)
              // links链接的起始节点进行判断,如果其id等于name则显示这类节点
              // 注意: graph=data
              for (var i = 0; i < links.length; i++) {
                // 如果links的起点等于name，并且终点等于正在处理的则显示
                if (links[i]['source'].properties.name === name && links[i]['target']
                  .id === d.id) {
                  return ''
                }
                if (links[i]['target'].properties.name === name && links[i]['source']
                  .id === d.id) {
                  return ''
                }
              }
              return 'inactive' // 前面CSS定义 .nodes circle.inactive
            }
          })

          d3.select('#svg1 .texts').selectAll('text').attr('class', function (d) {
            if (d.properties.name === name) {
              return ''
            }

            for (var i = 0; i < links.length; i++) {
              if (links[i]['source'].properties.name === name && links[i]['target']
                .id === d.id) {
                return ''
              }
              if (links[i]['target'].properties.name === name && links[i]['source']
                .id === d.id) {
                return ''
              }
            }
            return 'inactive'
          })
          d3.select('#svg1 .links').selectAll('line').attr('class', function (d) {
            if (d.source.properties.name === name || d.target.properties.name === name) {
              return ''
            } else {
              return 'inactive'
            }
          })
        }
      })
      // 鼠标移出文本还原相应节点及边
      $('#svg1').on('mouseleave', '.texts text', function (event) {
        if (!dragging) {
          d3.select('#svg1 .nodes').selectAll('circle').attr('class', '')
          d3.select('#svg1 .texts').selectAll('text').attr('class', '')
          d3.select('#svg1 .links').selectAll('line').attr('class', '')
        }
      })
      // 搜索框中输入内容则响应该事件
      // keyup按键敲击响应event
      $('#search input').keyup(function (event) {
        // 如果Input值是空的显示所有的圆和线(没有进行筛选)
        if ($(this).val() === '') {
          d3.select('#svg1 .texts').selectAll('text').attr('class', '')
          d3.select('#svg1 .nodes').selectAll('circle').attr('class', '')
          d3.select('#svg1 .links').selectAll('line').attr('class', '')
        } else { // 否则判断判断三个元素是否等于name值，等于则显示该值
          var name = $(this).val()
          // 搜索所有的节点
          d3.select('#svg1 .nodes').selectAll('circle').attr('class', function (d) {
            // 输入节点id的小写等于name则显示，否则隐藏
            if (d.properties.name.indexOf(name) >= 0) {
              return ''
            } else {
              // 优化：与该搜索节点相关联的节点均显示
              // links链接的起始节点进行判断,如果其id等于name则显示这类节点
              // 注意: graph=data
              for (var i = 0; i < links.length; i++) {
                // 如果links的起点等于name，并且终点等于正在处理的则显示
                if ((links[i]['source'].properties.name.indexOf(name) >= 0) &&
                  (links[i]['target'].id === d.id)) {
                  return ''
                }
                // 如果links的终点等于name，并且起点等于正在处理的则显示
                if ((links[i]['target'].properties.name.indexOf(name) >= 0) &&
                  (links[i]['source'].id === d.id)) {
                  return ''
                }
              }
              return 'inactive' // 隐藏
            }
          })
          // 搜索texts
          d3.select('#svg1 .texts').selectAll('text').attr('class', function (d) {
            if (d.properties.name.indexOf(name) >= 0) {
              return ''
            } else {
              // 优化：与该搜索节点相关联的节点均显示
              // links链接的起始节点进行判断,如果其id等于name则显示这类节点
              // 注意: graph=data
              for (var i = 0; i < links.length; i++) {
                // 如果links的起点等于name，并且终点等于正在处理的则显示
                if ((links[i]['source'].properties.name.indexOf(name) >= 0) &&
                  (links[i]['target'].id === d.id)) {
                  return ''
                }
                // 如果links的终点等于name，并且起点等于正在处理的则显示
                if ((links[i]['target'].properties.name.indexOf(name) >= 0) &&
                  (links[i]['source'].id === d.id)) {
                  return ''
                }
              }
              return 'inactive'
            }
          })
          // 搜索links
          // 显示相的邻边 注意 ||
          // name=$(this).val()：名字为键盘输入的内容
          d3.select('#svg1 .links').selectAll('line').attr('class', function (d) {
            if ((d.source.properties.name.indexOf(name) >= 0) ||
              (d.target.properties.name.indexOf(name) >= 0)
            ) {
              return ''
            } else {
              return 'inactive' // 隐藏
            }
          })
        }
      })
    },

    getdata () {
      nodes = []
      links = []
      data = []
      nodeSet = []
      const URL = 'http://localhost:8001/jsonData/' + id
      axios({
        url: URL,
        method: 'GET'
      })
        .then(response => {
          data = response.data
          console.log('data:', data)

          for (let item of data) {
            for (let segment of item.p.segments) {
              if (nodeSet.indexOf(segment.start.identity) === -1) {
                nodeSet.push(segment.start.identity)
                nodes.push({
                  id: segment.start.identity,
                  label: segment.start.labels[0],
                  properties: segment.start.properties
                })
              }
              if (nodeSet.indexOf(segment.end.identity) === -1) {
                nodeSet.push(segment.end.identity)
                nodes.push({
                  id: segment.end.identity,
                  label: segment.end.labels[0],
                  properties: segment.end.properties
                })
              }
              links.push({
                source: segment.relationship.start,
                target: segment.relationship.end,
                type: segment.relationship.type,
                properties: segment.relationship.properties
              })
            }
          }
          console.log('nodes:', nodes)
          console.log('links:', links)
        })
        .catch(error => {
          console.error('Error:', error)
        })

      setTimeout(() => {
        this.generateGraph()
      }, 500)
    }

  }
}

</script>

<style >
@import "../../../static/css/KnowledgeGraph.css";
</style>
